package com.tomtop.core;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceUtils;
import org.springframework.util.StringUtils;

import com.tomtop._const.Const;
import com.tomtop.core.model.SysUser;
import com.tomtop.core.utils.ContextUtils;

/**
 * 操作上下文
 * 
 * @author Administrator 请勿在service层使用
 */
public final class Context {
	private final static ThreadLocal<Context> contextHolder = new ThreadLocal<Context>() {
		protected Context initialValue() {
			return new Context();
		};
	};

	private static Logger logger = LoggerFactory.getLogger(Context.class);

	private Map<String, Object> contextMap;

	private HttpServletResponse response;

	private HttpServletRequest request;

	/**
	 * 当前线程中得消息
	 */
	private String message = null;
	/**
	 * 处理请求开始的时间,毫秒，
	 */
	private Long beginTime = null;

	/**
	 * 当前操作的标题
	 */
	private String title = null;
	private String clientid = null;
	private String host;
	private String callback = null;
	/**
	 * 当前登录的用户，如果存在(线程用户 )
	 */
	private Optional<SysUser> user = null;
	private String hostname = null;
	private String requestUri = null;
	private String userid = null;
	private Boolean ispc = null;
	private Boolean isapp = null;
	private Boolean isRest = false;

	public static Context getContext() {
		return contextHolder.get();
	}

	public static Context init(HttpServletRequest request, HttpServletResponse response) {
		if (request == null)
			return null;
		Context context = getContext();
		context.request = request;
		context.response = response;
		context.beginTime = System.currentTimeMillis();
		context.host = request.getHeader("Host");
		context.clientid = null;
		context.user = null;
		context.hostname = null;
		context.requestUri = null;
		context.userid = null;
		context.ispc = null;
		context.isapp = null;
		context.isRest = false;
		return context;
	}

	public static boolean isPc() {
		Context context = getContext();
		if (context.ispc != null)
			return context.ispc;
		if (context.request == null)
			return context.ispc = true;
		Device currentDevice = DeviceUtils.getCurrentDevice(context.request);
		return getContext().ispc = (currentDevice.isNormal()
				&& (context.host == null || !context.host.toLowerCase().startsWith("m")));
	}

	public static boolean isLogin() {
		Optional<SysUser> user = getUser();
		return user.isPresent() && user.get().isLogin();
	}

	public void setUserid(String userid) {
		this.userid = userid;
	}

	public static void sendUserId() {
		String userid = Context.getCookie(Const.USERID_COOKIE_NAME);
		if (userid == null) {
			userid = UUID.randomUUID().toString().replaceAll("-", "");
			Context.addCookie(Const.USERID_COOKIE_NAME, userid);
		}
	}

	public static String getCallback() {
		Context context = getContext();
		if (context.callback == null && context.request != null)
			context.callback = context.request.getParameter("jsoncallback");
		return context.callback;
	}

	public static String getHostName() {
		Context context = getContext();
		if (context.hostname != null)
			return context.hostname;
		int endIndex;
		int beginIndex;
		if (context.host != null && 0 < (endIndex = context.host.lastIndexOf('.'))
				&& 0 <= (beginIndex = context.host.lastIndexOf('.', endIndex - 1)) && 1 < (endIndex - beginIndex))
			return (context.hostname = context.host.substring(beginIndex + 1, endIndex));
		return (context.hostname = "");
	}

	public static String getHost() {
		return getContext().host;
	}

	public static void isRest(boolean isResEntity) {
		getContext().isRest = isResEntity;
	}

	public static boolean isRest() {
		return getContext().isRest;
	}

	private final static String error_uri_key = "javax.servlet.error.request_uri";

	public static String getRequestUri() {
		Context context = getContext();
		if (context.requestUri != null)
			return context.requestUri;
		if (context.request == null)
			return context.requestUri = "";
		String error_uri = (String) context.request.getAttribute(error_uri_key);
		if (error_uri != null)
			return (context.requestUri = error_uri);
		else
			return (context.requestUri = context.request.getRequestURI());
	}

	public static void remove() {
		// String title=getTitle();
		// String message=getMessage();
		contextHolder.remove();
	}

	public static Optional<SysUser> getUser() {
		Context context = getContext();
		if (context.user != null)
			return context.user;
		if (context.request == null)
			return context.user = SysUser.empty;
		return ContextUtils.initLoginInfo();
	}

	public static boolean isApp() {
		Context context = getContext();
		if (context.isapp != null)
			return context.isapp;
		return context.isapp = ContextUtils.isApp();
	}

	public static void setUser(Optional<SysUser> user) {
		getContext().user = user;
	}

	public static long getBeginTime() {
		return getContext().beginTime;
	}

	public static String getMessage() {
		return getContext().message;
	}

	public static void setMessage(String message) {
		getContext().message = message;
	}

	public static String getTitle() {
		return getContext().title;
	}

	public static void setTitle(String titleformat, Object... arg1) {
		getContext().title = String.format(titleformat, arg1);
	}

	public static void setRequest(HttpServletRequest request) {
		getContext().request = request;
	}

	public static void setResponse(HttpServletResponse response) {
		getContext().response = response;
	}

	public static HttpServletRequest getRequest() {
		return getContext().request;
	}

	public static HttpSession getSession() {
		HttpServletRequest request = getRequest();
		if (request == null)
			return null;
		for (int i = 0; i < 3; i++)
			try {
				return request.getSession();
			} catch (Exception e) {
				e.printStackTrace();
			}
		return null;

	}

	public static HttpServletResponse getResponse() {
		return getContext().response;
	}

	/**
	 * 设置值全部设置的request
	 * 
	 * @param key
	 * @param value
	 */
	public static void put(String key, Object value) {
		Context context = getContext();
		if (context.request != null)
			context.request.setAttribute(key, value);
		else {
			if (context.contextMap == null)
				context.contextMap = new HashMap<>();
			context.contextMap.put(key, value);
		}
	}

	/**
	 * 首先从request中取，取不到从请求参数中取，继续道请求头中取，继续到会话中取，继续到ServletContext中取,继续到redis中取,如果依然取不到，就返回null
	 * 
	 * @param key
	 * @return
	 */
	public static Object get(String key) {
		Context context = getContext();
		Object value = null;
		if (context.request != null && (value = context.request.getAttribute(key)) != null)
			return value;
		if (context.contextMap != null && (value = context.contextMap.get(key)) != null)
			return value;
		if (context.request == null)
			return value;
		if ((value = context.request.getParameter(key)) != null)
			return value;
		if ((value = context.request.getHeader(key)) != null)
			return value;
		HttpSession session = context.request.getSession();
		if ((value = session.getAttribute(key)) != null)
			return value;
		return getCookie(key);
	}

	public static String getCookie(String name) {
		if (StringUtils.isEmpty(name)) {
			logger.info("Cookie Name 为空 ：CookieName={}", name);
			return null;
		}
		HttpServletRequest request = getRequest();
		Cookie[] cookies = request.getCookies();
		if (cookies == null) {
			logger.info("没有取到 Cookie ：CookieName={}", name);
			return null;
		}
		for (Cookie cookie : cookies) {
			if (name.equals(cookie.getName()))
				try {
					String cookie_value = cookie.getValue();
					cookie_value = URLDecoder.decode(cookie_value, Const.DEFAULT_CHARSET.name());
					logger.debug(" Cookie中取到值 ：CookieName={},CookieValue={}", name, cookie_value);
					return cookie_value;
				} catch (UnsupportedEncodingException e) {
					logger.warn("读取cookie name={},出现异常", name, e);
				}
		}

		logger.debug(" Cookie中没有取到值 ：CookieName={}", name);
		return null;
	}

	public static void addCookie(String name, String value) {
		Context.addCookie(name, value, null, null, null, null);
	}

	public static void addCookie(String name, String value, String domain) {
		Context.addCookie(name, value, domain, null, null, null);
	}

	public static void addCookie(String name, String value, String domain, Integer maxage) {
		Context.addCookie(name, value, domain, maxage, null, null);
	}

	public static void addCookie(String name, String value, String domain, Integer maxage, Boolean httpOnly,
			Integer version) {
		if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
			return;
		}
		HttpServletResponse response = getResponse();
		try {
			Cookie cookie = new Cookie(name, URLEncoder.encode(value, Const.DEFAULT_CHARSET.name()));
			if (domain != null || (domain = getDefaultDomain()) != null)
				cookie.setDomain(domain);
			if (maxage == null)
				maxage = Integer.MAX_VALUE;
			cookie.setMaxAge(maxage);
			if (httpOnly != null)
				cookie.setHttpOnly(httpOnly);
			if (version != null)
				cookie.setVersion(version);
			cookie.setPath("/");
			response.addCookie(cookie);

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

	private static String getDefaultDomain() {
		String host = Context.getHost();
		String hostName = Context.getHostName();
		if (host != null && hostName != null)
			return host.substring(host.lastIndexOf(hostName + "."));
		return null;
	}

	public static void delCookie(String name) {
		delCookie(name, null);
	}

	public static void delCookie(String name, String domain) {
		if (name == null)
			return;
		if (domain == null && (domain = getDefaultDomain()) == null)
			return;
		HttpServletResponse response = getResponse();
		logger.debug("即将从cookie删除{},", name);
		Cookie delcookie = new Cookie(name, "");
		delcookie.setPath("/");
		if (domain != null)
			delcookie.setDomain(domain);
		delcookie.setMaxAge(0);
		response.addCookie(delcookie);
	}

	/***
	 * 获取客户端IP地址;这里通过了Nginx获取;X-Real-IP,
	 * 
	 * @return
	 */
	private final static String[] keys = new String[] { "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP",
			"WL-Proxy-Client-IP" };

	public static String getClientIP() {
		HttpServletRequest request = getRequest();
		if (request == null)
			return null;
		for (String key : keys) {
			String ip = request.getHeader(key);
			if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip))
				return ip.split(",")[0];
		}
		return request.getRemoteAddr();
	}

	/**
	 * 获取请求的客户端ip链路
	 * 
	 * @return
	 */
	public static String getClientIPLink() {
		HttpServletRequest request = getRequest();
		if (request == null)
			return null;
		return request.getHeader(keys[0]);
	}

	/**
	 * 客户端转发至redirecturi
	 */
	public static void sendRedirect(String redirecturi) {
		logger.info("请求转发至" + redirecturi);
		HttpServletResponse response = getResponse();
		if (response == null)
			return;
		try {
			response.sendRedirect(redirecturi);
		} catch (IOException e) {
			logger.warn("请求转发至" + redirecturi + "出现异常", e);
		}
	}

	/**
	 * 服务器端转发至
	 * 
	 * @param forwarduri
	 */
	public static void forward(String forwarduri) {
		logger.info("请求转发至" + forwarduri);
		HttpServletRequest request = getRequest();
		if (request == null)
			return;
		try {
			request.getRequestDispatcher(forwarduri).forward(request, getResponse());
		} catch (IOException | ServletException e) {
			logger.warn("请求转发至" + forwarduri + "出现异常", e);
		}
	}

	public static String getUserId() {
		if (isLogin())
			return getUser().get().getUserId();
		String userid = getContext().userid;
		if (userid != null)
			return userid;
		return getCookie(Const.USERID_COOKIE_NAME);
	}

	public static String getClientid() {
		Context context = getContext();
		if (context.clientid == null)
			context.clientid = context.request.getHeader("client");
		return context.clientid;
	}

	public static void setClientId(String clientId) {
		getContext().clientid = clientId;
	}

	public static String getScheme() {
		return getRequest().getScheme();
	}

	@Override
	public String toString() {
		return new StringBuffer("\r\n请求上下文信息  \r\n").append(" 请求地址:").append(getHost()).append(getRequestUri())
				.append(" 请求来源:").append(get("Referer")).append(" 客户端信息:").append(get("User-Agent")).append(" 客户端编号:")
				.append(getClientid()).append(" 客户端IP:").append(getClientIP()).append(" 客户端ip链路:")
				.append(getClientIPLink()).append(" 上下文描述信息:").append(this.message).toString();
	}
}
